#include "CvxDxutFrame.h"
#include <iostream>
#include "cvxImage.h"
#include <conio.h>
#include "exp_data.h"

using namespace std;

CDXUTDialogResourceManager  CvxDxutFrame::s_dialogResourceManager;
CDXUTDialog                 CvxDxutFrame::s_hud;                  // dialog for standard controls
CDXUTDialog                 CvxDxutFrame::s_sampleUI;

ID3DXFont*                  CvxDxutFrame::s_pFont9 = NULL;
CDXUTTextHelper*            CvxDxutFrame::s_pTxtHelper = NULL;
ID3DXSprite*                CvxDxutFrame::s_pSprite9 = NULL;
//IDirect3DSurface9*	        CvxDxutFrame::s_ImageSrc = NULL; //Source stereo image
//IDirect3DDevice9*           CvxDxutFrame::s_d3ddev = NULL;
//int					        CvxDxutFrame::s_ImageWidth = 1920/2;
//int					        CvxDxutFrame::s_ImageHeight = 1080/2;
//CvxSurface *                CvxDxutFrame::s_pSurface = NULL;
CvxStereoSurface *         CvxDxutFrame::s_pStereoSurface = NULL;
CvxCursorSurface *         CvxDxutFrame::s_pCursor = NULL;
bool                       CvxDxutFrame::s_isFullScreen = false;
ExpImagePos	*			   CvxDxutFrame::s_pExpImagePos = NULL;
KeyBoardControl *          CvxDxutFrame::s_keyBuffer = NULL;
CDXUTButton*               CvxDxutFrame::s_pPeakButton = NULL;
CDXUTButton*               CvxDxutFrame::s_pValleyButton = NULL;

extern CDXUTDirectionWidget g_LightControl;
extern CModelViewerCamera   g_Camera;


#define IDC_TOGGLEFULLSCREEN    1
#define IDC_PEAK                2
#define IDC_VALLEY              3

//--------------------------------------------------------------------------------------
// Initialize the app 
//--------------------------------------------------------------------------------------

/*

*/

void CvxDxutFrame::InitApp()
{
	//    g_SettingsDlg.Init( &g_DialogResourceManager );
	s_hud.Init( &CvxDxutFrame::s_dialogResourceManager );
	s_sampleUI.Init( &CvxDxutFrame::s_dialogResourceManager );

	s_hud.SetCallback( OnGUIEvent ); int iY = 10;
	s_hud.AddButton( IDC_TOGGLEFULLSCREEN, L"Toggle full screen (F3)", 35, iY, 125, 22,  VK_F3);
	s_hud.AddButton( IDC_PEAK, L"PEAK", 35, iY += 24+24, 125, 22, 0, false, &s_pPeakButton);
	s_hud.AddButton( IDC_VALLEY, L"VALLEY", 35, iY += 24, 125, 22, 0, false, &s_pValleyButton);

	s_pValleyButton->OnMouseLeave();
	s_pPeakButton->OnMouseLeave();

	s_sampleUI.SetCallback( OnGUIEvent ); iY = 10;

	char fileName[] = "IA90_64_4.txt";  //data_32_2.txt

	FILE *pf = fopen(fileName, "r");
	if (!pf)
	{
		_cprintf("fatal error: can not open %s in the current directory.\n", fileName);
		_cprintf("press any key to exit.\n");
		getch();
		exit(1); 
	}
	else
	{
		fclose(pf);
	}
	
	s_pExpImagePos = new ExpImagePos(string(fileName));

	string s1, s2;
	s_pExpImagePos->getImages(s1, s2);

	s_pStereoSurface = new CvxStereoSurface(s1, s2);

	s_keyBuffer = new KeyBoardControl;

	//set new cursor
	s_pCursor = new CvxCursorSurface("cursor//64_cross.png");

//	HCURSOR hs = LoadCursor(NULL, IDC_CROSS);
//	SetCursor(hs);	
}

void CvxDxutFrame::RenderText()
{
	s_pTxtHelper->Begin();
	s_pTxtHelper->SetInsertionPos( 5, 5 );
	s_pTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
	s_pTxtHelper->DrawTextLine( DXUTGetFrameStats( DXUTIsVsyncEnabled() ) );
	s_pTxtHelper->DrawTextLine( DXUTGetDeviceStats() );	
	s_pTxtHelper->End();
}
void CvxDxutFrame::RenderKeyboardHint()
{
	s_pTxtHelper->Begin();
	s_pTxtHelper->SetInsertionPos( 5, 5 );
	s_pTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
	s_pTxtHelper->DrawTextLine(L"keyboard:");
	s_pTxtHelper->DrawTextLine(L"Up arrow: peak");
	s_pTxtHelper->DrawTextLine(L"Down arrow: valley");
	s_pTxtHelper->DrawTextLine(L"Space: confirm");
	s_pTxtHelper->End();
}

void CvxDxutFrame::RenderImageCount(int num, int totalNum)
{
	WCHAR buf[256];
	swprintf_s(buf, 256, L"%d / %d", num, totalNum);
	s_pTxtHelper->Begin();
	s_pTxtHelper->SetInsertionPos( 5, 10 );
	s_pTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
	s_pTxtHelper->DrawTextLine(buf);	
	s_pTxtHelper->End();
}

void CvxDxutFrame::RenderExpConfiguration(bool isStereo, bool isSquare, bool isCaustic, char pOrv)
{
	WCHAR buf[256];
	swprintf_s(buf, 256, L"st: %d sq: %d cau: %d pOrv: %c", isStereo, isSquare, isCaustic, pOrv);
	s_pTxtHelper->Begin();
	s_pTxtHelper->SetInsertionPos( 5, 30 );
	s_pTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
	s_pTxtHelper->DrawTextLine(buf);	
	s_pTxtHelper->End();
}

void CvxDxutFrame::RenderThanks()
{
	s_pTxtHelper->Begin();
	s_pTxtHelper->SetInsertionPos( 5, 5 );
	s_pTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
	s_pTxtHelper->DrawTextLine(L"Thanks for participating the experiment!");
	s_pTxtHelper->End();
}

void CvxDxutFrame::saveResult()
{
	s_pExpImagePos->saveResult("result.txt");
	s_keyBuffer->_finish = true;
}

int CvxDxutFrame::Width()
{
	return 960;

}
int CvxDxutFrame::Height()
{
	return 540;
}

//--------------------------------------------------------------------------------------
// Handle messages to the application
//--------------------------------------------------------------------------------------
LRESULT CALLBACK CvxDxutFrame::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing,
									    void* pUserContext )
{
	// Pass messages to dialog resource manager calls so GUI state is updated correctly
	*pbNoFurtherProcessing = s_dialogResourceManager.MsgProc( hWnd, uMsg, wParam, lParam );
	if( *pbNoFurtherProcessing )
		return 0;

 	switch(uMsg)
 	{
 	case WM_SETCURSOR :
		{
			IDirect3DDevice9* pd3dDevice = DXUTGetD3D9Device();
			CvxDxutFrame::s_pCursor->loadSurface(pd3dDevice);
			pd3dDevice->SetCursorProperties(0, 0, CvxDxutFrame::s_pCursor->m_cursorSurface);
			CvxDxutFrame::s_pCursor->releaseSurface();
		}		
 		break;
	case WM_MOUSEMOVE :
	//	return FALSE;   //prevent mouse move
		break;
 	}
	



	// Give the dialogs a chance to handle the message first
	*pbNoFurtherProcessing = s_hud.MsgProc( hWnd, uMsg, wParam, lParam );
	if( *pbNoFurtherProcessing )
		return 0;
	*pbNoFurtherProcessing = s_sampleUI.MsgProc( hWnd, uMsg, wParam, lParam );
	if( *pbNoFurtherProcessing )
		return 0;

	g_LightControl.HandleMessages( hWnd, uMsg, wParam, lParam );

	// Pass all remaining windows messages to camera so it can respond to user input
	g_Camera.HandleMessages( hWnd, uMsg, wParam, lParam );

	// Pass all remaining windows messages to camera so it can respond to user input
//	s_pPeakButton->HandleKeyboard(uMsg, wParam, lParam);
//	s_pValleyButton->HandleKeyboard(uMsg, wParam, lParam);

	return 0;
}

void CALLBACK CvxDxutFrame::OnKeyboard( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
{
	if (s_keyBuffer->_finish == true)
	{
		_cprintf("the experiment is finished!\n");
		return ;
	}
	if (bKeyDown)
	{
		switch(nChar)
		{
				
			case VK_SPACE:
				{
					if (s_pPeakButton->m_bMouseOver == false && s_pValleyButton->m_bMouseOver == false)
					{
						s_keyBuffer->_inputError = true;
					}

					if (s_keyBuffer->_inputError == false)
					{
						s_pExpImagePos->recordButton(s_keyBuffer->_curInput);
						_cprintf("space is pressed %u\n", nChar);
						bool isNext = s_pExpImagePos->nextPos();
						if (isNext)
						{
							string s1, s2;
							s_pExpImagePos->getImages(s1, s2);
							s_pStereoSurface->updateImage(s1, s2);
							_cprintf("image name %s \n%s\n", s1.c_str(), s2.c_str());
						}
						else
						{
							saveResult();
							RenderThanks();
							s_keyBuffer->_finish = true;
						}
						if(s_pPeakButton && s_pValleyButton)
						{
							s_pPeakButton->OnMouseLeave();
							s_pValleyButton->OnMouseLeave();														
						}
					}					
				}
				break;

// 			case 0x50:   //P
// 				s_keyBuffer->_inputError = false;
// 				s_keyBuffer->_curInput = 'p';				
// 				break;
// 
// 			case 0x56:   //V				
// 				s_keyBuffer->_inputError = false;
// 				s_keyBuffer->_curInput = 'v';
// 				break;

			case VK_UP: // VK_LEFT VK_UP
				s_keyBuffer->_inputError = false;
				if(s_pPeakButton && s_pValleyButton)
				{
					s_pValleyButton->OnMouseLeave();
					s_pPeakButton->OnMouseEnter();
					s_keyBuffer->_curInput = 'P';					
				}
				break;

			case VK_DOWN: // VK_RIGHT  VK_DOWN
				s_keyBuffer->_inputError = false;
				if(s_pPeakButton && s_pValleyButton)
				{
					s_pPeakButton->OnMouseLeave();
					s_pValleyButton->OnMouseEnter();
					s_keyBuffer->_curInput = 'V';
				}
				break;


		
			default:
				s_keyBuffer->_inputError = true;
				break;

		}
	}

	//bool CDXUTButton::HandleKeyboard( UINT uMsg, WPARAM wParam, LPARAM lParam )

	
}
void CALLBACK CvxDxutFrame::OnMouse( bool bLeftButtonDown, bool bRightButtonDown, bool bMiddleButtonDown, 
					  bool bSideButton1Down, bool bSideButton2Down, int nMouseWheelDelta, int xPos, int yPos, void* pUserContext )
{

}

//--------------------------------------------------------------------------------------
// Handle updates to the scene.  This is called regardless of which D3D API is used
//--------------------------------------------------------------------------------------
void CALLBACK CvxDxutFrame::OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )
{
	g_Camera.FrameMove( fElapsedTime );
}

//--------------------------------------------------------------------------------------
// Rejects any D3D9 devices that aren't acceptable to the app by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK CvxDxutFrame::IsD3D9DeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat,
									 D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext )
{
	// Skip backbuffer formats that don't support alpha blending
	IDirect3D9* pD3D = DXUTGetD3D9Object();
	if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
		AdapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING,
		D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
		return false;

	// No fallback defined by this app, so reject any device that 
	// doesn't support at least ps2.0
	if( pCaps->PixelShaderVersion < D3DPS_VERSION( 2, 0 ) )
		return false;

	return true;
}

//--------------------------------------------------------------------------------------
// Create any D3D9 resources that will live through a device reset (D3DPOOL_MANAGED)
// and aren't tied to the back buffer size
//--------------------------------------------------------------------------------------
HRESULT CALLBACK CvxDxutFrame::OnD3D9CreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
									void* pUserContext )
{
	HRESULT hr;

	V_RETURN( s_dialogResourceManager.OnD3D9CreateDevice( pd3dDevice ) );
	//  V_RETURN( g_SettingsDlg.OnD3D9CreateDevice( pd3dDevice ) );

	V_RETURN( D3DXCreateFont( pd3dDevice, 30, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET,
		OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
		L"Arial", &s_pFont9 ) );

	float fObjectRadius = 1.0;
	V_RETURN( CDXUTDirectionWidget::StaticOnD3D9CreateDevice( pd3dDevice ) );
	g_LightControl.SetRadius( fObjectRadius );

	// Read the D3DX effect file
	WCHAR str[MAX_PATH];
	DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE;
#ifdef DEBUG_VS
	dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
#endif
#ifdef DEBUG_PS
	dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
#endif
	//V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"SimpleSample.fx" ) );
	// V_RETURN( D3DXCreateEffectFromFile( pd3dDevice, str, NULL, NULL, dwShaderFlags,
	//                                    NULL, &g_pEffect9, NULL ) );

	// Setup the camera's view parameters
	D3DXVECTOR3 vecEye( 0.0f, 0.0f, -15.0f );
	D3DXVECTOR3 vecAt ( 0.0f, 0.0f, -0.0f );
	g_Camera.SetViewParams( &vecEye, &vecAt );
	g_Camera.SetRadius( fObjectRadius * 3.0f, fObjectRadius * 0.5f, fObjectRadius * 10.0f );


	return S_OK;
}

//--------------------------------------------------------------------------------------
// Create any D3D9 resources that won't live through a device reset (D3DPOOL_DEFAULT) 
// or that are tied to the back buffer size 
//--------------------------------------------------------------------------------------
HRESULT CALLBACK CvxDxutFrame::OnD3D9ResetDevice( IDirect3DDevice9* pd3dDevice,
								   const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
{
	HRESULT hr;

	V_RETURN( s_dialogResourceManager.OnD3D9ResetDevice() );
	//   V_RETURN( g_SettingsDlg.OnD3D9ResetDevice() );

	if( s_pFont9 ) V_RETURN( s_pFont9->OnResetDevice() );
	//	if( g_pEffect9 ) V_RETURN( g_pEffect9->OnResetDevice() );

	V_RETURN( D3DXCreateSprite( pd3dDevice, &s_pSprite9 ) );
	s_pTxtHelper = new CDXUTTextHelper( s_pFont9, s_pSprite9, NULL, NULL, 30 );

	// Setup the camera's projection parameters
	float fAspectRatio = pBackBufferSurfaceDesc->Width / ( FLOAT )pBackBufferSurfaceDesc->Height;
	g_Camera.SetProjParams( D3DX_PI / 4, fAspectRatio, 2.0f, 4000.0f );
	g_Camera.SetWindow( pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height );
	g_Camera.SetButtonMasks( MOUSE_LEFT_BUTTON, MOUSE_WHEEL, MOUSE_MIDDLE_BUTTON );

	s_hud.SetLocation( pBackBufferSurfaceDesc->Width - 170, 0 );
	s_hud.SetSize( 170, 170 );
	s_sampleUI.SetLocation( pBackBufferSurfaceDesc->Width - 170, pBackBufferSurfaceDesc->Height - 350 );
	s_sampleUI.SetSize( 170, 300 );

	g_LightControl.OnD3D9ResetDevice( pBackBufferSurfaceDesc );		

	return S_OK;
}

//--------------------------------------------------------------------------------------
// Render the scene using the D3D9 device
//--------------------------------------------------------------------------------------
void CALLBACK CvxDxutFrame::OnD3D9FrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
	HRESULT hr;

	// Clear the render target and the zbuffer 
	V( pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB( 0, 45, 50, 170 ), 1.0f, 0 ) );

	// Render the scene
	if( SUCCEEDED( pd3dDevice->BeginScene() ) )
	{
		
		// set destRect
		s_pStereoSurface->loadSurface(pd3dDevice, s_isFullScreen);		

		// Get the Backbuffer then Stretch the Surface on it.
		IDirect3DSurface9 *backBuf = NULL;
		pd3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuf);  //add reference number
		pd3dDevice->StretchRect(s_pStereoSurface->m_surface, NULL, backBuf, &s_pStereoSurface->m_rect, D3DTEXF_NONE);

		s_pStereoSurface->releaseSurface();
		backBuf->Release();	



		DXUT_BeginPerfEvent( DXUT_PERFEVENTCOLOR, L"HUD / Stats" ); // These events are to help PIX identify what the code is doing
		//RenderText();
		V( s_hud.OnRender( fElapsedTime ) );
		V( s_sampleUI.OnRender( fElapsedTime ) );
		DXUT_EndPerfEvent();

		
		D3DXMATRIXA16 mWorld;
		D3DXMATRIXA16 mView;
		D3DXMATRIXA16 mProj;
		D3DXMATRIXA16 g_mCenterWorld;

		D3DXMatrixTranslation( &g_mCenterWorld, 0, 0, 0 );

		mWorld = g_mCenterWorld * *g_Camera.GetWorldMatrix();
		mProj = *g_Camera.GetProjMatrix();
		mView = *g_Camera.GetViewMatrix();

		//render probe
		D3DXCOLOR arrowColor = D3DXCOLOR( 1, 1, 0, 1 ); 
	//	V( g_LightControl.OnRender9( arrowColor, &mView, &mProj, g_Camera.GetEyePt()) );

		if (s_isFullScreen)
		{
			int pos_x, pos_y;
			s_pExpImagePos->getCursorPos(pos_x, pos_y);
			pos_x = std::max(0, pos_x-32);
			pos_y = std::max(0, pos_y-32);
			pd3dDevice->SetCursorPosition(pos_x, pos_y, 0);  
			pd3dDevice->ShowCursor(TRUE);
		}
		if (s_keyBuffer->_inputError)
		{
			RenderKeyboardHint();
		}
		else if(!s_keyBuffer->_finish)
		{
			//render number
			int totalNum = 256;
			int restNum = 0;
			s_pExpImagePos->GetRestNumber(totalNum, restNum);
			CvxDxutFrame::RenderImageCount(restNum, totalNum);
			
#if 0       //only for testing ground truth   
			bool isStereo  = false; 
			bool isSquare  = false;
			bool isCaustic = false;
			char pOrv = 'P';
			s_pExpImagePos->getConfiguration(isStereo, isSquare, isCaustic, pOrv);
			CvxDxutFrame::RenderExpConfiguration(isStereo, isSquare, isCaustic, pOrv);
#endif
		}

		if (s_keyBuffer->_finish)
		{
			RenderThanks();
		}

		

		V( pd3dDevice->EndScene() );
	}
}

//--------------------------------------------------------------------------------------
// Release D3D9 resources created in the OnD3D9ResetDevice callback 
//--------------------------------------------------------------------------------------

void CALLBACK CvxDxutFrame::OnD3D9LostDevice( void* pUserContext )
{
	s_dialogResourceManager.OnD3D9LostDevice();
	if( s_pFont9 ) s_pFont9->OnLostDevice();
	SAFE_RELEASE( s_pSprite9 );
	SAFE_DELETE( s_pTxtHelper );

	//add
	CDXUTDirectionWidget::StaticOnD3D9LostDevice();
	
}

//--------------------------------------------------------------------------------------
// Release D3D9 resources created in the OnD3D9CreateDevice callback 
//--------------------------------------------------------------------------------------
void CALLBACK CvxDxutFrame::OnD3D9DestroyDevice( void* pUserContext )
{
	s_dialogResourceManager.OnD3D9DestroyDevice();
	SAFE_RELEASE(s_pFont9 );

	//add
	SAFE_RELEASE(s_pSprite9);
	SAFE_DELETE( s_pTxtHelper);
	 CDXUTDirectionWidget::StaticOnD3D9DestroyDevice();
}

//--------------------------------------------------------------------------------------
// Handles the GUI events
//--------------------------------------------------------------------------------------
void CALLBACK CvxDxutFrame::OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext )
{
	switch( nControlID )
	{
	case IDC_TOGGLEFULLSCREEN:
		DXUTToggleFullScreen();
		s_isFullScreen = !s_isFullScreen;
		break;
		//   case IDC_TOGGLEREF:
		//        DXUTToggleREF(); break;
		//    case IDC_CHANGEDEVICE:
		//       g_SettingsDlg.SetActive( !g_SettingsDlg.IsActive() ); break;
	}
}